package jbitarrays;

import jbitarrays.api.CrawlerReader;
import jbitarrays.api.CrawlerWriter;
import jbitarrays.api.InfiniteBitArray;

/**
 * some utilities to help work with {@link InfiniteBitArray}s
 * 
 * @since 1.0
 */
public final class Utils {
	private Utils() {}

	/**
	 * Allows you to read a {@link Long} instead of an {@link Integer} out of a
	 * {@link DefaultCrawlerReader}.
	 * 
	 * @param n
	 *            n >= 0 && n <= {@link Long#SIZE}
	 * @since 1.0
	 */
	public static long getNextN(CrawlerReader r, int n) {
		if (n < 0 || n > Long.SIZE) {
			throw new IllegalArgumentException();
		}
		if (n <= Integer.SIZE) {
			int bits = r.getNextN(n);
			long ret = bits & 0x7FFFFFFF;
			long sign = bits >>> (Integer.SIZE - 1);
			ret |= sign << (Integer.SIZE - 1);
			return ret;
		} else {
			int bitsLeft = n - Integer.SIZE;

			long first32 = r.getNextN(bitsLeft);
			long ret = first32 << Integer.SIZE;

			int last32 = r.getNextN(Integer.SIZE);
			ret |= last32 & 0x7FFFFFFF;
			long sign = last32 >>> Integer.SIZE - 1;
			ret |= sign << (Integer.SIZE - 1);
			return ret;
		}
	}

	/**
	 * Allows you to write up to {@link Long#SIZE} bits of a {@link Long} into a
	 * {@link DefaultCrawlerReader}.
	 * 
	 * @param n
	 *            n >= 0 && n <= {@link Long#SIZE}
	 * @since 1.0
	 */
	public static void setNextN(CrawlerWriter w, int n, long l) {
		if (n < 0 || n > Long.SIZE) {
			throw new IllegalArgumentException();
		}
		if (n <= Integer.SIZE) {
			int it = (int) (l & ((1L << n) - 1));
			w.setNextN(n, it);
		} else {
			int bitsLeft = n - Integer.SIZE;
			int bitsLeftMask = bitsLeft == Integer.SIZE ? 0xFFFFFFFF : ((1 << bitsLeft) - 1);
			int top32 = ((int) (l >> Integer.SIZE)) & bitsLeftMask;
			int bottom32 = (int) (l & 0xFFFFFFFF);
			w.setNextN(bitsLeft, top32);
			w.setNextN(Integer.SIZE, bottom32);
		}
	}

	/**
	 * Helper for simple {@link CrawlerWriter} implementations.
	 */
	static void delegateSetNextNtoSetNext(CrawlerWriter w, int howMany, int value) {
		Utils.checkHowMany(howMany);
		for (int i = howMany - 1; i >= 0; --i) {
			w.setNext((value & (1 << i)) != 0);
		}
	}

	/**
	 * Useful validation of {@link CrawlerReader}, {@link CrawlerWriter} &
	 * {@link InfiniteBitArray} implementations
	 */
	static void checkHowMany(int howMany) {
		if (howMany < 0 || howMany > Integer.SIZE) {
			throw new IllegalArgumentException("Integer.SIZE >= howMany >= 0");
		}
	}

	/**
	 * Useful validation of {@link InfiniteBitArray} implementations
	 */
	static void checkIndex(int indexBits) {
		if (indexBits < 0) {
			throw new IllegalArgumentException("index >= 0");
		}
	}
}
